package org.brownflat.lighthouse.monitoring.xml;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.jaxen.JaxenException;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.XPath;
import org.jaxen.dom4j.Dom4jXPath;
import org.xml.sax.InputSource;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


public class XMLDocument {
    private Document document;
    private Map namespaceContexts = Collections.EMPTY_MAP;

    public XMLDocument(String xml) {
        init(xml);
    }

    public XMLDocument(Node node) {
        init(node.asXML());
    }

    private void init(String xml) {
        SAXReader reader = new SAXReader();
        try {
            byte[] bytes = xml.getBytes();
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            InputSource input = new InputSource();
            input.setByteStream(byteArrayInputStream);
            document = reader.read(input);
        } catch (DocumentException de) {
            throw new XMLDocumentException(de);
        }
    }

    public XMLDocument documentAt(String path) {
        try {
            XMLDocument newDocument = new XMLDocument((Node) getXPath(path).selectSingleNode(this.document));
            newDocument.setNamespaceContexts(namespaceContexts);
            return newDocument;
        } catch (JaxenException e) {
            throw new RuntimeException(e);
        }
    }

    public List<XMLDocument> documentsAt(String path) {
        List<XMLDocument> documents = new ArrayList<XMLDocument>();
        XPath xpath = getXPath(path);
        try {
            List nodes = xpath.selectNodes(this.document);
            for (Iterator iterator = nodes.iterator(); iterator.hasNext(); ) {
                Node node = (Node) iterator.next();
                XMLDocument newDocument = new XMLDocument(node);
                newDocument.setNamespaceContexts(namespaceContexts);
                documents.add(newDocument);
            }
            return documents;
        } catch (JaxenException e) {
            throw new RuntimeException(e);
        }
    }

    public String selectValueAt(String path) {
        Node result = selectNode(path);
        if (result == null) {
            return "";
        }
        return result.getText();
    }

    public String toXML() {
        return document.asXML();
    }

    public String toString() {
        return toXML();
    }

    public void setNamespaceContexts(Map<String, String> namespaceContexts) {
        this.namespaceContexts = namespaceContexts;
    }

    private XPath getXPath(String path) {

        XPath xpath = null;
        try {
            xpath = new Dom4jXPath(path);
        } catch (JaxenException e) {
            throw new RuntimeException(e);
        }
        xpath.setNamespaceContext(new SimpleNamespaceContext(namespaceContexts));
        return xpath;
    }

    private Node selectNode(String path) {
        try {
            return (Node) getXPath(path).selectSingleNode(this.document);
        } catch (JaxenException e) {
            throw new RuntimeException(e);
        }
    }
}
